
`include "common_header.verilog"

//  *************************************************************************
//  File : p8264_mld_rx_top.v
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2008 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Denis Poverennyy
//  info@morethanip.com
//  *************************************************************************
//  Description : The MLD Top Block consists of the following blocks:
//  -Register Block. 
//  -Read State Machine. -- 
//  -Marker State Machine. 
//  -MLD Reorder. 
//  -Hi BER Controller. 
//  -Reset Controller
//  Version     : $Id: p8264_mld_rx_top.v,v 1.9 2016/07/13 06:58:13 dk Exp $
//  *************************************************************************

module p8264_mld_rx_top (
   
        reset_rxclk,
        cgmii_rxclk,
        sw_reset,
        desk_buf_0_data,
        desk_buf_1_data,
        desk_buf_2_data,
        desk_buf_3_data,
        desk_buf_rd,
        desk_buf_rd_vec,
        mld_rst,
        mld_rst_done,
        disable_mld,
        ena_hi_ber25,
        lane_active,
        vl_intvl,
        vl_0_enc,
        vl_1_enc,
        vl_2_enc,
        vl_3_enc,
        loopback_ena,
        cgmii_txclk_ena,
        signal_det,
        block_lock,
        desk_buf_full,
        desk_buf_aempty,
        desk_buf_empty,
        data_val,
        data_type,
        data_out,

        lane_map,
        align_done,
        align_done_l,
        bip8_err,
        ber_count_read,
        r_test_mode,
        hi_ber,
        ber_count,
        ber_timer_short,
        ber_timer_done
        
`ifdef MTIPPCS82_EEE_ENA 
        ,
        ram_period,
        first_rx_lpi_active,
        rx_lpi_active,
        rx_down_count,
        rx_down_count_val,
        mld_start
`endif        
        );

input           reset_rxclk;            //  async active high reset
input           cgmii_rxclk;            //  cgmii clock
input           sw_reset;               //  software reset
input   [66:0]  desk_buf_0_data;        //  buffer 0 data
input   [66:0]  desk_buf_1_data;        //  buffer 1 data
input   [66:0]  desk_buf_2_data;        //  buffer 2 data
input   [66:0]  desk_buf_3_data;        //  buffer 3 data
output          desk_buf_rd;            //  read from the deskew buffer
output  [3:0]   desk_buf_rd_vec;        //  read from the deskew buffer (one bit per lane)
output          mld_rst;                //  sw reset to deskew buffer and serdes 
input   [3:0]   mld_rst_done;           //  sw reset done 
input           disable_mld;            //  disable MLD (10G/25G mode setting)
input           ena_hi_ber25;           //  Enable Hi-Ber Measure Interval for 25G Mode
input   [3:0]   lane_active;		//  Current active lanes
input   [15:0]  vl_intvl;               //  Virtual Lane Marker Interval Setting
input   [23:0]  vl_0_enc;               //  Marker pattern for PCS Virtual Lane 0
input   [23:0]  vl_1_enc;               //  Marker pattern for PCS Virtual Lane 1
input   [23:0]  vl_2_enc;               //  Marker pattern for PCS Virtual Lane 2
input   [23:0]  vl_3_enc;               //  Marker pattern for PCS Virtual Lane 3
input           loopback_ena;           //  XLGMII loopback enable
input           cgmii_txclk_ena;        //  XLGMII transmit clock enable
output  [3:0]   block_lock;             //  Block Lock in the reference clock domain
input   [3:0]   signal_det;             //  if high, the corresponding lane is OK  
input   [3:0]   desk_buf_full;          //  If high, the Deskew Buffer is in overflow state - read too slow
input   [3:0]   desk_buf_aempty;        //  deskew buffer almost empty
input   [3:0]   desk_buf_empty;         //  if low, at least one word is available in the Deskew Buffer
output          data_val;               //  data valid
output  [1:0]   data_type;              //  data 0 type (Data/Control)
output  [63:0]  data_out;               //  data 0 output

output  [7:0]   lane_map;               //  Lane mapping status
output          align_done;             //  Alignment for all lanes done
output  [3:0]   align_done_l;           //  Alignment done per lane        
output  [3:0]   bip8_err;               //  bip error pulse bit per lane
input           ber_count_read;         //  BER_count counter has been read
input           r_test_mode;            //  Receive test pattern
output          hi_ber;                 //  High Bit Error 
output  [21:0]  ber_count;              //  BER_count counter to be read
input           ber_timer_short;        //  speed up for test mode
output          ber_timer_done; 

`ifdef MTIPPCS82_EEE_ENA 

input           ram_period;             // if set the RAM is expected;
input           first_rx_lpi_active;    // Boolean variable, first_rx_lpi_active is set true when the receiver entered state RX_LPI_ACTIVE.
input           rx_lpi_active;          // A Boolean variable that is set to true when the receiver is in a low power state. 
output   [7:0]  rx_down_count;          // The value that results from the bit-wise exclusive-OR of the Count Down (CD3) byte and the M0 of RAM (see 82.2.8a).
output          rx_down_count_val;      // if 1, the rx_down_count is valid  
output          mld_start;              // mld reorder starts as soon as deskew fifo not empty

`endif

//-------------------------------------
// Output Signals
//-------------------------------------
wire            desk_buf_rd;            
wire    [3:0]   desk_buf_rd_vec;        
wire            mld_rst;                
wire    [3:0]   block_lock;             
wire            data_val;               
wire    [1:0]   data_type;              
wire    [63:0]  data_out;               
wire    [7:0]   lane_map;               
wire            align_done;             
wire    [3:0]   align_done_l;           
wire    [3:0]   bip8_err;               
wire            hi_ber;                 
wire    [21:0]  ber_count;              
wire            ber_timer_done;
`ifdef MTIPPCS82_EEE_ENA 
wire     [7:0]  rx_down_count;          
wire            rx_down_count_val;      
wire            mld_start;             
`endif

//-------------------------------------
// Internal Signals
//-------------------------------------
wire            desk_buf_rd_int;            //  read from the deskew buffer
wire    [66:0]  desk_buf_0_data_r;      // Deskewdata from the RG for Lane 0
wire    [66:0]  desk_buf_1_data_r;      // Deskewdata from the RG for Lane 1 
wire    [66:0]  desk_buf_2_data_r;      // Deskewdata from the RG for Lane 2 
wire    [66:0]  desk_buf_3_data_r;      // Deskewdata from the RG for Lane 3 
wire            desk_buf_data_val;      // write deskew data into the RG
wire            desk_buf_data_val_r;    // deskew data available in the RG
wire    [3:0]   desk_buf_data_val_vec;
wire            ber_reset;              // BER logic reset if either no alignment or test mode
wire            ber_block_lock;         // BER relevant block lock indication
wire            align_done_int;         // the same align_done
wire            align_lost_comb;        // at least for one lane Alignment was lost
wire    [3:0]   vl_map_0;               // Virtual Lane 0 Mapping to physical one
wire    [3:0]   vl_map_1;               // Virtual Lane 1 Mapping to physical one 
wire    [3:0]   vl_map_2;               // Virtual Lane 2 Mapping to physical one 
wire    [3:0]   vl_map_3;               // Virtual Lane 3 Mapping to physical one 
wire            desk_buf_data_mld_adv;  // data to rearder MLD are valid, 1 cycle advance before desk_buf_data_mld_dval
wire            desk_buf_data_mld_dval; // data to rearder MLD are valid
wire            desk_buf_marker_dval;   // marker to rearder MLD are valid
reg             desk_buf_marker_dval_r; // marker to rearder MLD are valid
reg             desk_buf_marker_dval_r2;// marker to rearder MLD are valid
wire   [3:0]    block_lock_int;         // Block Lock gated with signal_det
reg             mld_start_int;
wire            data_val_re;            //  data valid from reordering
wire    [1:0]   data_type_re;           //  data 0 type (Data/Control)
wire    [63:0]  data_out_re;            //  data 0 output


wire    [1:0]   sh_0_final;             // sync headers for Hi Ber Calculation Lane 0 (only control blocks and block lock to be set)
wire    [1:0]   sh_1_final;             // sync headers for Hi Ber Calculation Lane 1 (only control blocks and block lock to be set)
wire    [1:0]   sh_2_final;             // sync headers for Hi Ber Calculation Lane 2 (only control blocks and block lock to be set)
wire    [1:0]   sh_3_final;             // sync headers for Hi Ber Calculation Lane 3 (only control blocks and block lock to be set)
wire            mld_rst_int;            // the same as mld_rst
wire    [3:0]   bip8_err_init;          // BIP error vector from the BIP Check block
wire    [7:0]   rx_down_count_0;        // down count value for Lane 0 derived from the BIP fields
wire    [7:0]   rx_down_count_1;        // down count value for Lane 1 derived from the BIP fields
wire    [7:0]   rx_down_count_2;        // down count value for Lane 2 derived from the BIP fields
wire    [7:0]   rx_down_count_3;        // down count value for Lane 3 derived from the BIP fields

wire    [3:0] 	mld_rst_done_int;

`ifdef MTIPPCS82_EEE_ENA 
wire    [3:0]   desk_read_extra;        // one bit per lane, if set corresponding buffer should  be extra read for one clock
wire            read_adjust_req;        // one clock pulse - if set, the markers should be adjussted as reflected in desk_read_extra
reg             mld_rst_int_r;          // one clock delayed mld_rst_int.          
reg             mld_reset_ignore;       // In order not to lose the alignment lock, the first reset after lpi detection must be suppressed. 
wire            marker_sm_lpi_reset;    // mld_rst_int gated with mld_reset_ignore

wire sync_reset_lpi = first_rx_lpi_active || sw_reset; //  software reset from the Registers or lpi is detected (sync reset for the Read SM)

assign mld_start = mld_start_int;
`endif


assign mld_rst = mld_rst_int; 

assign block_lock = {desk_buf_3_data_r[66], desk_buf_2_data_r[66], desk_buf_1_data_r[66], desk_buf_0_data_r[66]};

// no need to reset for block lock lost for the single lane without MLD
assign block_lock_int = {( desk_buf_3_data_r[66] & signal_det[3] & ~disable_mld), (desk_buf_2_data_r[66] & signal_det[2] & ~disable_mld) , 
                         ( desk_buf_1_data_r[66] & signal_det[1] & ~disable_mld), (desk_buf_0_data_r[66] & signal_det[0] & ~disable_mld)};


//  Register Block (memory output register)

rg_40g #(66) U_RG_40G (
        .reset_rxclk                    (reset_rxclk),
        .cgmii_rxclk                    (cgmii_rxclk),
        .sw_reset                       ({4{mld_rst_int}}),
        .desk_buf_0_data                (desk_buf_0_data[65:0]),
        .desk_buf_1_data                (desk_buf_1_data[65:0]),
        .desk_buf_2_data                (desk_buf_2_data[65:0]),
        .desk_buf_3_data                (desk_buf_3_data[65:0]),
        .desk_buf_data_val              (desk_buf_data_val_vec),
        .desk_buf_0_data_r              (desk_buf_0_data_r[65:0]),
        .desk_buf_1_data_r              (desk_buf_1_data_r[65:0]),
        .desk_buf_2_data_r              (desk_buf_2_data_r[65:0]),
        .desk_buf_3_data_r              (desk_buf_3_data_r[65:0])
                                                                );



// The MSB (block-lock) is registered but with data_val to latch it. In case of "strange" memory.

rg_40g #(1) U_RG_40G67 (
        .reset_rxclk                    (reset_rxclk),
        .cgmii_rxclk                    (cgmii_rxclk),
        .sw_reset                       ({sw_reset,sw_reset,sw_reset,sw_reset}),
        .desk_buf_0_data                (desk_buf_0_data[66]),
        .desk_buf_1_data                (desk_buf_1_data[66]),
        .desk_buf_2_data                (desk_buf_2_data[66]),
        .desk_buf_3_data                (desk_buf_3_data[66]),
        .desk_buf_data_val              ({4{desk_buf_data_val}}),
        .desk_buf_0_data_r              (desk_buf_0_data_r[66]),
        .desk_buf_1_data_r              (desk_buf_1_data_r[66]),
        .desk_buf_2_data_r              (desk_buf_2_data_r[66]),
        .desk_buf_3_data_r              (desk_buf_3_data_r[66])
                                                                );


mld_read_sm_40g_64b U_MLD_READ_SM (	

        .reset_rxclk                    (reset_rxclk),                  
        .cgmii_rxclk                    (cgmii_rxclk),                  
        .mld_rst                        (mld_rst_int),                  
        .vl_intvl                       (vl_intvl), 
        .disable_mld                    (disable_mld),                    
`ifdef MTIPPCS82_EEE_ENA 
        .ram_period                     (ram_period),
        .desk_read_extra                (desk_read_extra),
        .read_adjust_req                (read_adjust_req),
`endif
        .buf_aempty                     (desk_buf_aempty),
        .buf_empty                      (desk_buf_empty),
        .desk_buf_rd                    (desk_buf_rd_int),
        .desk_buf_rd_vec                (desk_buf_rd_vec),
        .desk_buf_data_val              (desk_buf_data_val),            
        .desk_buf_data_val_r            (desk_buf_data_val_r),          
        .desk_buf_data_val_vec          (desk_buf_data_val_vec),
        .desk_buf_data_val_vec_r        (),                 
        .desk_buf_data_mld_adv          (desk_buf_data_mld_adv),
        .desk_buf_data_mld_dval         (desk_buf_data_mld_dval),
        .desk_buf_marker_dval           (desk_buf_marker_dval) 
                                                                );
assign desk_buf_rd             = desk_buf_rd_int;


`ifdef MTIPPCS82_EEE_ENA

// In order not to lose the alignment lock, the first reset after lpi detection must be suppressed.

always @(posedge cgmii_rxclk or  posedge reset_rxclk)
begin
        if (reset_rxclk==1'b 1)
        begin
                mld_reset_ignore        <= 1'b0;
                mld_rst_int_r           <= 1'b0;
        end        
        else 
        begin
                mld_rst_int_r           <= mld_rst_int;
                if (first_rx_lpi_active == 1'b 1 && sw_reset == 1'b0)
                begin
                        mld_reset_ignore <= 1'b1;
                end
                else if (mld_rst_int_r == 1'b 1 && mld_rst_int == 1'b 0)
                begin
                        mld_reset_ignore <= 1'b0;
                end
        end
end

assign marker_sm_lpi_reset = mld_rst_int & ~mld_reset_ignore;

`endif


p8264_marker_sm_top U_MARKER_SM_TOP (

        .reset                          (reset_rxclk),                         
        .clk                            (cgmii_rxclk),                           
`ifdef MTIPPCS82_EEE_ENA 
        .mld_rst                        (marker_sm_lpi_reset),
`else
        .mld_rst                        (mld_rst_int),
`endif                        
        .desk_buff_data_0               (desk_buf_0_data_r[65:0]),  
        .desk_buff_data_1               (desk_buf_1_data_r[65:0]),  
        .desk_buff_data_2               (desk_buf_2_data_r[65:0]),  
        .desk_buff_data_3               (desk_buf_3_data_r[65:0]),  
        .desk_buff_data_val             (desk_buf_data_val_r),
        .desk_buf_marker_dval           (desk_buf_marker_dval), 
        .vl_0_enc                       (vl_0_enc),
        .vl_1_enc                       (vl_1_enc),
        .vl_2_enc                       (vl_2_enc),
        .vl_3_enc                       (vl_3_enc),
        .block_lock                     (block_lock_int),       //  block lock status per lane
        .align_done                     (align_done_int),                 
        .align_done_l                   (align_done_l),                 
        .align_lost_comb                (align_lost_comb),           
        .vl_map_0                       (vl_map_0),
        .vl_map_1                       (vl_map_1),
        .vl_map_2                       (vl_map_2),
        .vl_map_3                       (vl_map_3)
`ifdef MTIPPCS82_EEE_ENA 
        ,
        .ram_period                     (ram_period),
        .first_rx_lpi_active            (first_rx_lpi_active)
`endif           
                                                                );


p8264_bip_check_top U_BIP_CHECK_TOP(

        .reset                          (reset_rxclk),          
        .clk                            (cgmii_rxclk),            
        .data_val                       (desk_buf_data_val_r   ),       
        .marker_dval                    (desk_buf_marker_dval),    
        .data_in_0                      (desk_buf_0_data_r[65:2]),      
        .data_in_1                      (desk_buf_1_data_r[65:2]),      
        .data_in_2                      (desk_buf_2_data_r[65:2]),      
        .data_in_3                      (desk_buf_3_data_r[65:2]),      
        .sh_in_0                        (desk_buf_0_data_r[1:0]),        
        .sh_in_1                        (desk_buf_1_data_r[1:0]),        
        .sh_in_2                        (desk_buf_2_data_r[1:0]),        
        .sh_in_3                        (desk_buf_3_data_r[1:0]),
        .align_done_l                   (align_done_l),
        .bip8_err                       (bip8_err_init   )        
        
`ifdef MTIPPCS82_EEE_ENA         
        ,
        .vl_match_num_0                 (vl_map_0),                    
        .vl_match_num_1                 (vl_map_1),
        .vl_match_num_2                 (vl_map_2),
        .vl_match_num_3                 (vl_map_3),
        .rx_down_count_0                (rx_down_count_0),
        .rx_down_count_1                (rx_down_count_1),
        .rx_down_count_2                (rx_down_count_2),
        .rx_down_count_3                (rx_down_count_3)
`endif
                                        );

`ifdef MTIPPCS82_EEE_ENA   


p8264_eee_down_counter U_EEE_DOWN_COUNT (

        .reset                          (reset_rxclk),                  
        .clk                            (cgmii_rxclk),                    
        .mld_rst                        (mld_rst_int),                
        
        .bip8_err_in                    (bip8_err_init),            
        .ram_period                     (ram_period),             
        .first_rx_lpi_active            (first_rx_lpi_active),    
        .align_done                     (align_done_int),
                                
        .rx_down_count_0                (rx_down_count_0),
        .rx_down_count_1                (rx_down_count_1),
        .rx_down_count_2                (rx_down_count_2),
        .rx_down_count_3                (rx_down_count_3),        
        .rx_down_count_val              (desk_buf_marker_dval_r2),      
        
        .rx_down_count_final            (rx_down_count),                                    
        .rx_down_count_final_val        (rx_down_count_val),
        .desk_read_extra                (desk_read_extra),        
        .read_adjust_req                (read_adjust_req),        
        .bip8_err_out                   (bip8_err)           );

`else

assign bip8_err = bip8_err_init;

`endif


assign align_done = align_done_int; 


always @(posedge cgmii_rxclk or  posedge reset_rxclk)
begin
        if (reset_rxclk==1'b 1)
        begin
                mld_start_int <= 1'b0;
                desk_buf_marker_dval_r  <= 1'b0;
                desk_buf_marker_dval_r2 <= 1'b0;
        end        
        else 
        begin
                desk_buf_marker_dval_r  <= desk_buf_marker_dval;
                desk_buf_marker_dval_r2 <= desk_buf_marker_dval_r;
                if (mld_rst_int == 1'b 1 || disable_mld == 1'b 1)
                begin
                        mld_start_int <= 1'b0;
                end
                else if (desk_buf_rd_int==1'b 1)
                begin
                        mld_start_int <= 1'b1;
                end
        end
end

mld_rx_reorder_40g_64b U_MLD_RX_REORDER (
        .reset_rxclk                    (reset_rxclk),
        .rxclk                          (cgmii_rxclk),
        .desk_buf_0_data                (desk_buf_0_data_r[65:0]), //  Data input 
        .desk_buf_1_data                (desk_buf_1_data_r[65:0]), //  Data input 
        .desk_buf_2_data                (desk_buf_2_data_r[65:0]), //  Data input 
        .desk_buf_3_data                (desk_buf_3_data_r[65:0]), //  Data input 
        .vl_map_0                       (vl_map_0),
        .vl_map_1                       (vl_map_1),
        .vl_map_2                       (vl_map_2),
        .vl_map_3                       (vl_map_3),
        .data_vld                       (desk_buf_data_val_r),
        .mld_data_vld_adv               (desk_buf_data_mld_adv),
        .mld_data_vld                   (desk_buf_data_mld_dval),
        .align_done                     (mld_start_int), 
        .loopback_ena                   (loopback_ena),         //  XLGMII loopback enable
        .cgmii_txclk_ena                (cgmii_txclk_ena),      //  XLGMII transmit clock enable
        //  data to De-Scrambler
        .data_val                       (data_val_re),          //  data valid      
        .data_type_out                  (data_type_re),         //  data 0 type (Data/Control)
        .data_out                       (data_out_re),          //  data 0 output      
        .lane_map                       (lane_map)
                                                        );

// bypass re-ordering in non-MLD mode.
assign data_val  = disable_mld == 1'b 1 ? desk_buf_data_val_r       : data_val_re;
assign data_type = disable_mld == 1'b 1 ? desk_buf_0_data_r[1:0]    : data_type_re;
assign data_out  = disable_mld == 1'b 1 ? desk_buf_0_data_r[65:2]   : data_out_re;


// restart/sync reset control
assign mld_rst_done_int[0] = lane_active[0] == 1'b 0 ? 1'b 1 : mld_rst_done[0]; // Lane0 not used when inactive
assign mld_rst_done_int[1] = lane_active[1] == 1'b 0 ? 1'b 1 : mld_rst_done[1]; // Lane1 not used when inactive
assign mld_rst_done_int[2] = lane_active[2] == 1'b 0 ? 1'b 1 : mld_rst_done[2]; // Lane2 not used when inactive
assign mld_rst_done_int[3] = lane_active[3] == 1'b 0 ? 1'b 1 : mld_rst_done[3]; // Lane3 not used when inactive

rx_mld_rst_cntrl_40g RX_MLD_RST (	//  Lane mapping status

        .reset_rxclk                    (reset_rxclk),          //  async active high reset
        .cgmii_rxclk                    (cgmii_rxclk),          //  cgmii clock
`ifdef MTIPPCS82_EEE_ENA
        .sw_reset                       (sync_reset_lpi),       //  software reset from the Registers or lpi is detected
`else
        .sw_reset                       (sw_reset),             //  software reset from the Registers
`endif
        .desk_buf_full                  (desk_buf_full),        //  if any bit set, receive should be reset
        .align_lost                     (align_lost_comb),      //  at least for one lane Alignment was lost  
        .block_lock                     (block_lock_int),       //  block lock status per lane
        .mld_rst_done                   (mld_rst_done_int),         //  feedback signal from each lane.
        .mld_rst                        (mld_rst_int));


// Hi-Ber measurement before MLD

assign ber_block_lock = (disable_mld == 1'b 1) ? desk_buf_0_data_r[66] : align_done_int;        // Block-Lock for BER measurement

assign ber_reset = r_test_mode |                // Init condition for BER measurement
                `ifdef MTIPPCS82_EEE_ENA
                   rx_lpi_active |
                `endif 
                   ~ber_block_lock;

assign sh_0_final = (desk_buf_0_data_r[66]==1'b 1) ? desk_buf_0_data_r[1:0]: 2'b 01;
assign sh_1_final = (desk_buf_1_data_r[66]==1'b 1 && disable_mld == 1'b 0) ? desk_buf_1_data_r[1:0]: 2'b 01;
assign sh_2_final = (desk_buf_2_data_r[66]==1'b 1 && disable_mld == 1'b 0) ? desk_buf_2_data_r[1:0]: 2'b 01;
assign sh_3_final = (desk_buf_3_data_r[66]==1'b 1 && disable_mld == 1'b 0) ? desk_buf_3_data_r[1:0]: 2'b 01;

p8264_ber_acc U_BER_ACC (

        .reset                          (reset_rxclk),          //  async active high reset
        .clk                            (cgmii_rxclk),          //  system clock
        .ena_clause49                   (disable_mld),          //  Use Clause 49 defined constants
        .ena_hi_ber25                   (ena_hi_ber25),
        .ena_hi_ber5                    (1'b 0),
        .ber_reset                      (ber_reset),            //  either alignment lock lost or test_mode set (or both)
        .sh_val                         (desk_buf_data_val_r),
        .sh_0                           (sh_0_final),
        .sh_1                           (sh_1_final),
        .sh_2                           (sh_2_final),
        .sh_3                           (sh_3_final),
        .ber_timer_short                (ber_timer_short),
        .timer_done                     (ber_timer_done),
        .ber_count_read                 (ber_count_read),       //  BER_count counter has been read
        .hi_ber                         (hi_ber),               //  High Bit Error 
        .ber_count                      (ber_count));

endmodule // module p8264_mld_rx_top

